﻿using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;

namespace Helpers
{
    public class FilteredTextBox : IHtmlString
    {
        #region Filter types

        public enum FilterType
        {
            /// <summary>
            /// Allows only alphabetic characters from a to z and A to Z and space characters.
            /// </summary>
            AlphabeticCharacter,

            /// <summary>
            /// Allows only numbers.
            /// </summary>
            Numbers,

            /// <summary>
            /// Allows numbers and the decimal separator as defined by the current culture info.
            /// </summary>
            DecimalNumbers,

            /// <summary>
            /// Allows numbers, the decimal separator and the currency group separator as defined by the current culture info.
            /// </summary>
            DecimalNumbersWithGroupSeparator,

            /// <summary>
            /// Allows all the characters specified in the ValidCharacters field.
            /// </summary>
            CustomValidCharacters,

            /// <summary>
            /// Allows all the characters except the ones specified in the InvalidCharacters field.
            /// </summary>
            CustomInvalidCharacters
        }

        #endregion

        #region Properties

        public HtmlHelper HtmlHelper { get; set; }

        public FilterType Type { get; set; }

        public string Name { get; set; }

        public object Value { get; set; }

        public string ValidCharacters { get; set; }

        public string InvalidCharacters { get; set; }

        public IDictionary<string, object> HtmlAttributes { get; set; }

        #endregion

        #region Implementation of IHtmlString

        /// <summary>
        /// Returns an HTML-encoded string.
        /// </summary>
        /// <returns>
        /// An HTML-encoded string.
        /// </returns>
        public string ToHtmlString()
        {
            return Render();
        }

        #endregion

        #region Constructors

        internal FilteredTextBox(HtmlHelper htmlHelper, string name, FilterType type, string validCharacters, string invalidCharacters)
        {
            HtmlHelper = htmlHelper;
            Name = name;
            Type = type;
            ValidCharacters = validCharacters;
            InvalidCharacters = invalidCharacters;
        }

        #endregion

        #region Fluent configuration

        /// <summary>
        /// Set the value of the text box.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public FilteredTextBox SetValue(object value)
        {
            Value = value;
            return this;
        }

        /// <summary>
        /// Set the dictionary that contains the HTML attributes to set for the element.
        /// </summary>
        /// <param name="htmlAttributes"></param>
        /// <returns></returns>
        public FilteredTextBox SetHtmlAttributes(IDictionary<string, object> htmlAttributes)
        {
            HtmlAttributes = htmlAttributes;
            return this;
        }

        #endregion

        #region Methods

        public override string ToString()
        {
            return ToHtmlString();
        }

        private string Render()
        {
            var tagBuilder = new TagBuilder("input");

            if (HtmlAttributes != null)
                tagBuilder.MergeAttributes(HtmlAttributes);

            tagBuilder.MergeAttribute("type", "text");

            if (!string.IsNullOrEmpty(Name))
                tagBuilder.MergeAttribute("name", Name, true);

            var value = Value == null ? string.Empty : Value.ToString();
            var regex = string.Empty;
            switch (Type)
            {
                case FilterType.AlphabeticCharacter:
                    regex = "[^a-zA-Z ]";
                    value = Regex.Replace(value, regex, string.Empty);
                    break;
                case FilterType.Numbers:
                    regex = "[^0-9]";
                    value = Regex.Replace(value, regex, string.Empty);
                    break;
                case FilterType.DecimalNumbers:
                    regex = string.Format("[^0-9{0}]", Regex.Escape(System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator));
                    value = Regex.Replace(value, regex, string.Empty);
                    break;
                case FilterType.DecimalNumbersWithGroupSeparator:
                    regex = string.Format("[^0-9{0}{1}]", Regex.Escape(System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator), Regex.Escape(System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberGroupSeparator));
                    value = Regex.Replace(value, regex, string.Empty);
                    break;
                case FilterType.CustomValidCharacters:
                    regex = string.Format("[^{0}]", Regex.Escape(ValidCharacters));
                    value = Regex.Replace(value, regex, string.Empty);
                    break;
                case FilterType.CustomInvalidCharacters:
                    /*
                    regex = string.Join("|", InvalidCharacters.ToCharArray().Select(c => Regex.Escape(c.ToString(CultureInfo.InvariantCulture))));
                    replaced with the next line thanks to Siderite - http://siderite.blogspot.com/
                    */
                    regex = string.Format("[{0}]", Regex.Escape(InvalidCharacters));
                    value = Regex.Replace(value, regex, string.Empty);
                    break;
            }

            tagBuilder.MergeAttribute("data-filter", "1");
            tagBuilder.MergeAttribute("value", value, true);
            tagBuilder.MergeAttribute("data-regex", regex, true);

            if (!string.IsNullOrEmpty(Name))
            {
                // If there are any errors for a named field, we add the css attribute.
                ModelState modelState;
                if (HtmlHelper.ViewData.ModelState.TryGetValue(Name, out modelState))
                {
                    if (modelState.Errors.Count > 0)
                    {
                        tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
                    }
                }
            }

            return tagBuilder.ToString(TagRenderMode.SelfClosing);
        }

        #endregion
    }
}